package com.zs.main.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zs.framework.domain.Result;
import com.zs.common.dto.common.ApiMonitorRequestDTO;
import com.zs.common.dto.common.ApiMonitorResponseDTO;
import com.zs.common.dto.common.MonitorDTO;
import com.zs.common.utils.IPUtils;
import com.zs.common.utils.ServiceContext;
import com.zs.common.utils.ZSDateUtils;
import com.zs.main.config.InitializingApiMonitorConfiguration;
import com.zs.main.config.MyContentCachingRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Copyright 2020 ZS Network Co., Ltd. All rights reserved.
 * 本文件仅限于正数网络技术有限公司内部传阅，禁止外泄以及用于其他的商业目的。
 * Created by yangning on 2021/8/24.
 *
 * @description: 访问日志记录过滤器
 */
@Component
@Slf4j
public class ApiLogFilter extends OncePerRequestFilter implements Ordered {

    private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        /*
         *  0. 获取sn
         *  1. 开始时间
         *  2. 获取接口URI
         *  3. 获取接口中文名称(使用注解@ApiMonitor)
         *  4. 获取请求参数（header、params、body）
         *  5. 日志打印
         *  6. 向后传递
         */
        String responseBody = null;
        long startTimeMillis = System.currentTimeMillis();
        String startTime = ZSDateUtils.format(new Date(),ZSDateUtils.TIMESTAMP_FORMAT);
        String uri = httpServletRequest.getRequestURI();
        String sn = ServiceContext.getSN();
        log.info("Controller.log:[{}], 请求uri路径:[{}]", sn, uri);
        String uriName = null;
        String method = null;
        String token = null;
        String tenantId = null;
        String deviceId = null;
        String deviceType = null;
        ContentCachingResponseWrapper wrapperResponse = null;
        String requestBodyPost = null;
        String requestBodyGet = null;
        String endTime = null;
        Integer code = null;
        boolean success = false;
        try {
            boolean flag = StringUtils.equals("/", StringUtils.substring(uri, uri.length() - 1, uri.length()));
            if (flag) {
                uri = uri.substring(0, uri.length() - 1);
            }
            uriName = InitializingApiMonitorConfiguration.apiMonitorCache.get(uri.replaceAll("//", "/"));
            method = httpServletRequest.getMethod();
            // body
            wrapperResponse = new ContentCachingResponseWrapper(httpServletResponse);
            // params
            requestBodyPost = getBodyString(httpServletRequest.getReader());
            requestBodyGet = URLDecoder.decode(httpServletRequest.getQueryString() == null ? "" : httpServletRequest.getQueryString(), "utf-8");
        } catch (IOException e) {
            log.error("MonitorFilter request error....", e);
        } finally {
            // 下一链
            filterChain.doFilter(httpServletRequest, wrapperResponse);
        }

        long cost;
        try {
            responseBody = new String(wrapperResponse.getContentAsByteArray());
            Result result = JSON.toJavaObject(JSON.parseObject(responseBody), Result.class);
            result = result == null ? Result.failed("请求路径不存在") : result;
            success = result.isSuccess();
            code = result.getBizCode();
            /*
             *  7. 获取响应内容（body）
             *  8. 获取成败
             *  9.错误编码
             *  10.结束时间
             *  11.耗时
             *  12.日志打印
             *  13.日志其他处理方法：入库、elk或其他
             */
            long endTimeMillis = System.currentTimeMillis();
            endTime = ZSDateUtils.format(new Date(),ZSDateUtils.TIMESTAMP_FORMAT);
            cost = endTimeMillis - startTimeMillis;
            ApiMonitorRequestDTO apiMonitorRequestDTO = ApiMonitorRequestDTO.builder()
                    .XAccessToken(token)
                    .XTenantId(tenantId)
                    .XDeviceId(deviceId)
                    .XDeviceType(deviceType)
                    .contentType(httpServletRequest.getContentType())
                    .requestMethod(method)
                    .requestParam(requestBodyPost+requestBodyGet)
                    .sn(sn)
                    .build();
            MonitorDTO monitorDTO = MonitorDTO.builder()
                    .tenantId(tenantId)
                    .interfaceName(uriName)
                    .requestMapping(uri)
                    .statusCode(code)
                    .success(success ? 0 : 1)
                    .startTime(startTime)
                    .endTime(endTime)
                    .startTimeLong(startTimeMillis)
                    .endTimeLong(endTimeMillis)
                    .costTime(cost)
                    .visitorIp(IPUtils.getIpAddr(httpServletRequest))
                    .sn(sn)
                    .build();
            ApiMonitorResponseDTO apiMonitorResponseDTO = ApiMonitorResponseDTO.builder()
                    .responseParam(responseBody)
                    .sn(sn)
                    .build();
            log.info("api access info:{},request:{},response:{}",
                    JSONObject.toJSONString(monitorDTO),
                    JSONObject.toJSONString(apiMonitorRequestDTO),
                    JSONObject.toJSONString(apiMonitorResponseDTO));
        } catch (Exception e) {
            log.error("MonitorFilter response error....", e);
        } finally {
            wrapperResponse.copyBodyToResponse();
        }
    }

    /**
     * 判断是否post请求
     *
     * @param method 请求方式
     * @return
     */
    public boolean isPost(String method) {
        return HttpMethod.POST.matches(method);
    }

    private boolean isFormPost(HttpServletRequest httpServletRequest) {
        String contentType = httpServletRequest.getContentType();
        return (contentType != null && contentType.contains(FORM_CONTENT_TYPE) &&
                HttpMethod.POST.matches(httpServletRequest.getMethod()));
    }

    public static String getBodyString(BufferedReader br) {
        String inputLine;
        StringBuilder str = new StringBuilder();
        try {
            while ((inputLine = br.readLine()) != null) {
                str.append(inputLine);
            }
            br.close();
        } catch (IOException e) {
            log.error("IO Exception", e);
        }
        return str.toString();
    }

    @Override
    public int getOrder() {
        return 400;
    }
}
